/*  -*- mode: c++; coding: utf-8; c-file-style: "stroustrup"; -*-

    Copyright 2008 Asier Aguirre <asier.aguirre@gmail.com>
    This file is part of Intromit.

    Intromit is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Intromit is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Intromit.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef OGL_HH
#define OGL_HH
//! \defgroup introsight_framework Introsight 3D framework over Intromit
//@{

// INCLUDES
#include "util.hh"
#include "refcnt.hh"

#include <X11/Xlib.h>
#include <GL/gl.h>
#define GLX_GLXEXT_PROTOTYPES
#include <GL/glx.h>
#include <GL/glu.h>

// DEFINES
// some of this are default when window is created
#define OGL_NOEVENTS_EVENT         0
#define OGL_MOUSE_BUTTON_EVENT     (ButtonPressMask | ButtonReleaseMask)
#define OGL_MOUSE_MOVE_EVENT       (PointerMotionMask)
#define OGL_KEYBOARD_EVENT         (KeyPressMask | KeyReleaseMask)
#define OGL_EXPOSE_EVENT           (ExposureMask)
#define OGL_CONFIGURE_EVENT        (StructureNotifyMask)
#define OGL_ENTER_LEAVE_EVENT      (EnterWindowMask | LeaveWindowMask)
#define OGL_ALLX_EVENT             (OGL_MOUSE_BUTTON_EVENT | OGL_MOUSE_MOVE_EVENT | \
				    OGL_KEYBOARD_EVENT | OGL_EXPOSE_EVENT | OGL_CONFIGURE_EVENT | \
				    OGL_ENTER_LEAVE_EVENT)

// predefined cursors
#define OGL_CURSOR_IMPOSSIBLE       -1  // use it to set system default cursor
#define OGL_CURSOR_ZOOM              0
#define OGL_CURSOR_SELECTION         1
#define OGL_CURSOR_MOVE              2
#define OGL_CURSOR_CLOCK             3
#define OGL_CURSOR_SELECTOR          4
#define OGL_CURSOR_NONE              5
#define OGL_CURSOR_LAST              6  // use it to set application default cursor

class ogl;

//! Simple X Window with OpenGL context
class ogl_window: public reference_counter
{
    Window w;

    // status of the window
    char *name;
    bool managed;
    int x, y, width, height;
    ogl *o;
    int default_cursor;

    // PRIVATE METHODS
    void ctor();
    void dtor();

    Window create_X_window(bool managed);
    void destroy_window();
    ogl *check_ogl();
    void update_state_from_window();
    bool get_geometry(Window w, int *x, int *y, int *width, int *height, Window *root=NULL);
    static bool put_glx_ctx(Display *dpy, GLXContext glx_ctx, Window w);
    bool set_position_no_check(int x_, int y_);

    // extensions
    char *extensions_next(char *ext);
    char *extensions_only_one(char *ext);
    void extensions_print_common(char *needle=NULL);

public:
    ogl_window();
    virtual ~ogl_window();

    bool init();
    bool init(bool managed, char *name);
    bool init(ogl *o);
    bool init(ogl *o, bool managed, char *name);

    // getters
    inline ogl *get_ogl() {return o;}
    Display *get_dpy();
    inline Window get_window() {return w;}

    // events
    bool set_events(int mask); // use OGL_XXX_EVENT defines or X11 ones
    XEvent *get_event(XEvent *event, int mask);

    // typical window operations
    bool set_size(int width, int height);
    bool get_size(int *width, int *height);
    int get_width() {return width;}
    inline int get_height() {return height;}
    inline double get_ratio() {return (double)width/(double)height;}
    bool set_position(int x, int y);
    bool get_position(int *x, int *y);
    bool set_name(char *name);
    inline char *get_name() {return name;}
    void map();
    void sync();

    // relationship with other windows and utility functions
    bool get_root_size(int *x, int *y);
    bool reparent(Window parent, int x, int y);
    bool set_cursor(int c); // also sets default cursor

    // OpenGL
    bool set_gl_ctx();
    bool unset_gl_ctx();
    void gl_swap();
    void gl_test();
    void extensions_print(); // extensions
    void extensions_print_search(char *needle);
    char *extensions_search(char *needle);
};

//! OpenGL GLX interface
class ogl: public reference_counter
{
    Display *dpy;
    GLXContext glx_ctx;
    XVisualInfo *visinfo;
    Cursor *cursor;
    int n_cursor;

    // PRIVATE METHODS
    void ctor();
    void dtor();

    Display *check_dpy();
    GLXContext check_glx_ctx();
    XVisualInfo *check_visinfo();
    bool open_X_connection();
    bool create_glx_context(int z_buffer, int double_buffer);
    static int X_handler_error(Display *disp, XErrorEvent *error);
    static int X_handler_io_error(Display *disp);
    bool init_cursors();

public:
    ogl();
    virtual ~ogl();

    bool init();

    // returns a new initialized window
    ogl_window *new_window(bool managed, char *name);
    void sync();

    // keyboard repetition
    void keyboard_repetition(bool status);

    // turns on XSynchronize and sets IOError handlers
    void debug_mode();

    bool init_dpy(Display *dpy);
    inline Display *get_dpy() {return check_dpy();}
    inline Display *get_dpy_no_check() {return dpy;}
    inline GLXContext get_glx_ctx() {return check_glx_ctx();}
    inline XVisualInfo *get_visinfo() {return check_visinfo();}

    // return cursor
    inline Cursor get_cursor(int i) {return cursor[i];}
};

//@}
#endif

/* Offline metadata attributes:

   @metadata_attrs(
	ogl_window
	{
		# members
		name, managed, x, y,
		width, height, o,
		default_cursor:
						select, read_only

		# methods
		init,
		set_size, get_size,
		get_width, get_height,
		get_ratio,
		set_position, get_position,
		set_name, get_name,
		update_state_from_window,
		map, sync,
		get_root_size,
		set_cursor,
		set_gl_ctx, unset_gl_ctx,
		gl_swap, gl_test,
		extensions_print,
		extensions_print_search,
		extensions_search:
						select

		# special methods
		init:				postunpickle
		update_state_from_window:       prepickle
	}

	ogl
	{
		# methods
		ogl, init, keyboard_repetition,
		debug_mode:
						select

		# special methods
		init:				postunpickle
	}
   )
*/
